---
title: 'Fetch data with queries'
sidebar_title: '7. Fetch data with queries'
description: Working with the useQuery React Hook
---

import { MultiCodeBlock } from 'gatsby-theme-apollo-docs';
import Disclaimer from '../../shared/disclaimer.mdx';

> Time to accomplish: 10 minutes

Now that we've [set up Apollo Client](./client/), we can integrate it into our React app. This lets us use [React Hooks](https://reactjs.org/docs/hooks-intro.html) to bind the results of GraphQL queries directly to our UI.

> **If you're starting the tutorial here, first [clone the example app](./introduction/#clone-the-example-app).**

## Integrate with React

To connect Apollo Client to React, we wrap our app in the `ApolloProvider` component from the `@apollo/client` package. We pass our client instance to the `ApolloProvider` component via the `client` prop.

<Disclaimer />

Open `src/index.tsx` and replace its contents with the following:

<MultiCodeBlock>

```tsx:title=src/index.tsx
import {
  ApolloClient,
  NormalizedCacheObject,
  ApolloProvider
} from '@apollo/client';
import { cache } from './cache';
import React from 'react';
import ReactDOM from 'react-dom';
import Pages from './pages';
import injectStyles from './styles';

// Initialize ApolloClient
const client: ApolloClient<NormalizedCacheObject> = new ApolloClient({
  cache,
  uri: 'http://localhost:4000/graphql',
});

injectStyles();

// Pass the ApolloClient instance to the ApolloProvider component
ReactDOM.render(
  <ApolloProvider client={client}>
    <Pages />
  </ApolloProvider>,
  document.getElementById('root')
);
```

</MultiCodeBlock>

The `ApolloProvider` component is similar to React’s context provider: it wraps your React app and places `client` on the context, which enables you to access it from anywhere in your component tree.

Now we're ready to build React components that execute GraphQL queries.


## Display a list of launches

Let's build the page in our app that shows a list of available SpaceX launches. Open `src/pages/launches.tsx`. Right now, the file looks like this:

<MultiCodeBlock>

```tsx:title=src/pages/launches.tsx
import React, { Fragment, useState }  from 'react'; // preserve-line
import { RouteComponentProps } from '@reach/router';
import { gql } from '@apollo/client' // preserve-line

export const LAUNCH_TILE_DATA = gql`
  fragment LaunchTile on Launch {
    __typename
    id
    isBooked
    rocket {
      id
      name
    }
    mission {
      name
      missionPatch
    }
  }
`;

interface LaunchesProps extends RouteComponentProps {}

const Launches: React.FC<LaunchesProps> = () => {
  return <div />;
}

export default Launches;
```

</MultiCodeBlock>

### Define the query

First, we'll define the shape of the query we'll use to fetch a paginated list of launches. Paste the following below the declaration of `LAUNCH_TILE_DATA`:

```js:title=src/pages/launches.tsx
export const GET_LAUNCHES = gql`
  query GetLaunchList($after: String) {
    launches(after: $after) {
      cursor
      hasMore
      launches {
        ...LaunchTile
      }
    }
  }
  ${LAUNCH_TILE_DATA}
`;
```

#### Using fragments

Notice that our query definition pulls in the `LAUNCH_TILE_DATA` definition above it. `LAUNCH_TILE_DATA` defines a GraphQL [fragment](https://graphql.org/learn/queries/#fragments), which is named `LaunchTile`. A fragment is useful for defining a set of fields that you can include across multiple queries without rewriting them.

In the query above, we include the `LaunchTile` fragment in our query by preceding it with `...`, similar to JavaScript [spread syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax).


#### Pagination details

Notice that in addition to fetching a list of `launches`, our query fetches `hasMore` and `cursor` fields. That's because the `launches` query returns **paginated results**:

* The `hasMore` field indicates whether there are additional launches beyond the list returned by the server.
* The `cursor` field indicates the client's current position within the list of launches. We can execute the query again and provide our most recent `cursor` as the value of the `$after` variable to fetch the _next_ set of launches in the list.


### Apply the `useQuery` hook

We'll use Apollo Client's `useQuery` [React Hook](https://reactjs.org/docs/hooks-intro.html) to execute our new query within the `Launches` component. The hook's result object provides properties that help us populate and render our component throughout the query's execution.

1. Modify your `@apollo/client` import to include `useQuery`, and import a few predefined components for rendering the page:

  ```js:title=src/pages/launches.tsx
  import { gql, useQuery } from '@apollo/client';
  import { LaunchTile, Header, Button, Loading } from '../components';
  ```

  If you're using TypeScript, also import the necessary types that are generated from your server's schema definitions:

  ```js:title=src/pages/launches.tsx
  import * as GetLaunchListTypes from './__generated__/GetLaunchList';
  ```

2. Replace the dummy declaration of `const Launches` with the following:

  <MultiCodeBlock>

  ```tsx:title=src/pages/launches.tsx
  const Launches: React.FC<LaunchesProps> = () => {
    const {
      data,
      loading,
      error
    } = useQuery<
      GetLaunchListTypes.GetLaunchList,
      GetLaunchListTypes.GetLaunchListVariables
    >(GET_LAUNCHES);

    if (loading) return <Loading />;
    if (error) return <p>ERROR</p>;
    if (!data) return <p>Not found</p>;

    return (
      <Fragment>
        <Header />
        {data.launches &&
          data.launches.launches &&
          data.launches.launches.map((launch: any) => (
            <LaunchTile key={launch.id} launch={launch} />
          ))}
      </Fragment>
    );
  }
  ```

  </MultiCodeBlock>

This component passes our `GET_LAUNCHES` query to `useQuery` and obtains `data`, `loading`, and `error` properties from the result. Depending on the state of those properties, we render a list of launches, a loading indicator, or an error message.

Start up both your server and client with `npm start` and visit `localhost:3000`. If everything's configured correctly, our app's main page appears and lists 20 SpaceX launches!

We have a problem though: there are _more_ than 20 SpaceX launches in total. Our server paginates its results and includes a maximum of 20 launches in a single response.

To be able to fetch and store _all_ launches, we need to modify our code to use the `cursor` and `hasMore` fields included in our query. Let's learn how.

### Add pagination support

> Apollo Client 3 provides new pagination helper functions for [offset-based](https://www.apollographql.com/docs/react/pagination/offset-based/#the-offsetlimitpagination-helper) and [Relay-style](https://www.apollographql.com/docs/react/pagination/cursor-based/#relay-style-cursor-pagination) pagination that are not yet reflected in this tutorial.

Apollo Client provides a `fetchMore` helper function to assist with paginated queries. It enables you to execute the same query with different values for variables (such as the current cursor).

Add `fetchMore` to the list of objects we destructure from the `useQuery` result object, and also define an `isLoadingMore` state variable:

<MultiCodeBlock>

```tsx:title=src/pages/launches.tsx
const Launches: React.FC<LaunchesProps> = () => {
  const {
    data,
    loading,
    error,
    fetchMore // highlight-line
  } = useQuery<
    GetLaunchListTypes.GetLaunchList,
    GetLaunchListTypes.GetLaunchListVariables
  >(GET_LAUNCHES);
  const [isLoadingMore, setIsLoadingMore] = useState(false); //highlight-line
  // ...
}
```

</MultiCodeBlock>

Now we can connect `fetchMore` to a button within the `Launches` component that fetches additional launches when it's clicked.

Paste this code directly above the closing `</Fragment>` tag in the `Launches` component:

<MultiCodeBlock>

```tsx:title=src/pages/launches.tsx
{data.launches && data.launches.hasMore && (
  isLoadingMore
    ? <Loading />
    : <Button
        onClick={async () => {
          setIsLoadingMore(true);
          await fetchMore({
            variables: {
              after: data.launches.cursor,
            },
          });
          setIsLoadingMore(false);
        }}
      >
        Load More
      </Button>
)}
//</Fragment>
```

</MultiCodeBlock>

When our new button is clicked, it calls `fetchMore` (passing the current `cursor` as the value of the `after` variable) and displays a Loading notice until the query returns results.

Let's test our button. Start everything up and visit `localhost:3000` again. A **Load More** button now appears below our 20 launches. Click it. After the query returns, _no additional launches appear_. 🤔

If you check your browser's network activity, you'll see that the button did in fact send a followup query to the server, and the server did in fact respond with a list of launches. _However_, Apollo Client keeps these lists separate, because they represent the results of queries with _different variable values_ (in this case, the value of `after`).

We need Apollo Client to instead _merge_ the launches from our `fetchMore` query with the launches from our _original_ query. Let's configure that behavior.

### Merge cached results

Apollo Client stores your query results in its **in-memory cache**. The cache handles most operations intelligently and efficiently, but it doesn't automatically know that we want to merge our two distinct lists of launches. To fix this, we'll define a **`merge` function** for the paginated field in our schema.

Open `src/cache.ts`, where our default `InMemoryCache` is initialized:

<MultiCodeBlock>

```ts:title=src/cache.ts
//YOU DON'T NEED TO COPY THIS CODE
import { InMemoryCache, Reference } from '@apollo/client';

export const cache: InMemoryCache = new InMemoryCache({});
```

</MultiCodeBlock>

The schema field that our server paginates is the list of `launches`. Modify the initialization of `cache` to add a `merge` function for the `launches` field, like so:

<MultiCodeBlock>

```ts:title=src/cache.ts
export const cache: InMemoryCache = new InMemoryCache({
  typePolicies: {
    Query: {
      fields: {
        launches: {
          keyArgs: false,
          merge(existing, incoming) {
            let launches: Reference[] = [];
            if (existing && existing.launches) {
              launches = launches.concat(existing.launches);
            }
            if (incoming && incoming.launches) {
              launches = launches.concat(incoming.launches);
            }
            return {
              ...incoming,
              launches,
            };
          }
        }
      }
    }
  }
});
```

</MultiCodeBlock>

This `merge` function takes our `existing` cached launches and the `incoming` launches and combines them into a single list, which it then returns. The cache stores this combined list and returns it to all queries that use the `launches` field.

> This example demonstrates a use of [field policies](https://www.apollographql.com/docs/react/caching/cache-field-behavior/), which are cache configuration options that are specific to individual fields in your schema.

If you try clicking the **Load More** button now, the UI will successfully append additional launches to the list!

## Display a single launch's details

We want to be able to click a launch in our list to view its full details. Open `src/pages/launch.tsx` and replace its contents with the following:

<MultiCodeBlock>

```tsx:title=src/pages/launch.tsx
import { gql } from '@apollo/client';
import { LAUNCH_TILE_DATA } from './launches';

export const GET_LAUNCH_DETAILS = gql`
  query LaunchDetails($launchId: ID!) {
    launch(id: $launchId) {
      site
      rocket {
        type
      }
      ...LaunchTile
    }
  }
  ${LAUNCH_TILE_DATA}
`;
```

</MultiCodeBlock>

This query includes all the details we need for the page. Notice that we're reusing the `LAUNCH_TILE_DATA` fragment that's already defined in `launches.tsx`. 

Once again, we'll pass our query to the `useQuery` hook. This time, we also need to pass the corresponding launch's `launchId` to the query as a variable. The value of `launchId` is available as a prop that's passed by the router.

Now replace the contents of `launch.tsx` with the following:

<MultiCodeBlock>

```tsx:title=src/pages/launch.tsx
import React, { Fragment } from 'react'; // preserve-line
import { gql, useQuery } from '@apollo/client'; // preserve-line

import { LAUNCH_TILE_DATA } from './launches';
import { Loading, Header, LaunchDetail } from '../components'; // preserve-line
import { ActionButton } from '../containers'; // preserve-line
import { RouteComponentProps } from '@reach/router';
import * as LaunchDetailsTypes from './__generated__/LaunchDetails';

export const GET_LAUNCH_DETAILS = gql`
  query LaunchDetails($launchId: ID!) {
    launch(id: $launchId) {
      site
      rocket {
        type
      }
      ...LaunchTile
    }
  }
  ${LAUNCH_TILE_DATA}
`;

interface LaunchProps extends RouteComponentProps {
  launchId?: any;
}

const Launch: React.FC<LaunchProps> = ({ launchId }) => {
  const {
    data,
    loading,
    error,
  } = useQuery<
    LaunchDetailsTypes.LaunchDetails,
    LaunchDetailsTypes.LaunchDetailsVariables
  >(GET_LAUNCH_DETAILS,
    { variables: { launchId } }
  );

  if (loading) return <Loading />;
  if (error) return <p>ERROR: {error.message}</p>;
  if (!data) return <p>Not found</p>;

  return (
    <Fragment>
      <Header image={data.launch && data.launch.mission && data.launch.mission.missionPatch}>
        {data && data.launch && data.launch.mission && data.launch.mission.name}
      </Header>
      <LaunchDetail {...data.launch} />
      <ActionButton {...data.launch} />
    </Fragment>
  );
}

export default Launch;
```

</MultiCodeBlock>

Just like before, we use the status of the query to render either a `loading` or `error` state, or data when the query completes.

Return to your app and click a launch in the list to view its details page.


## Display the profile page

We want a user's profile page to display a list of launches that they've booked a seat on. Open `src/pages/profile.tsx` and replace its contents with the following:

<MultiCodeBlock>

```tsx:title=src/pages/profile.tsx
import React, { Fragment } from 'react'; // preserve-line
import { gql, useQuery } from '@apollo/client'; // preserve-line

import { Loading, Header, LaunchTile } from '../components'; // preserve-line
import { LAUNCH_TILE_DATA } from './launches'; // preserve-line
import { RouteComponentProps } from '@reach/router';
import * as GetMyTripsTypes from './__generated__/GetMyTrips';

export const GET_MY_TRIPS = gql`
  query GetMyTrips {
    me {
      id
      email
      trips {
        ...LaunchTile
      }
    }
  }
  ${LAUNCH_TILE_DATA}
`;

interface ProfileProps extends RouteComponentProps {}

const Profile: React.FC<ProfileProps> = () => {
  const {
    data,
    loading,
    error
  } = useQuery<GetMyTripsTypes.GetMyTrips>(
    GET_MY_TRIPS,
    { fetchPolicy: "network-only" } // highlight-line
  );
  if (loading) return <Loading />;
  if (error) return <p>ERROR: {error.message}</p>;
  if (data === undefined) return <p>ERROR</p>;

  return (
    <Fragment>
      <Header>My Trips</Header>
      {data.me && data.me.trips.length ? (
        data.me.trips.map((launch: any) => (
          <LaunchTile key={launch.id} launch={launch} />
        ))
      ) : (
        <p>You haven't booked any trips</p>
      )}
    </Fragment>
  );
}

export default Profile;
```

</MultiCodeBlock>

You should recognize all of the concepts in this code from the pages we've already completed, with one highlighted exception: we're setting a `fetchPolicy`.

### Customizing the fetch policy

As mentioned earlier, Apollo Client stores query results in its cache. If you query for data that's already present in your cache, Apollo Client can return that data without needing to fetch it over the network.

_However_, cached data can become stale. Slightly stale data is acceptable in many cases, but we definitely want our user's list of booked trips to be up to date. To handle this, we've specified a **fetch policy** for our `GET_MY_TRIPS` query.

A fetch policy defines how Apollo Client uses the cache for a particular query. The default policy is `cache-first`, which means Apollo Client checks the cache to see if the result is present before making a network request. If the result is present, no network request occurs.

By setting this query's fetch policy to `network-only`, we guarantee that Apollo Client _always_ queries our server to fetch the user's most up-to-date list of booked trips.

> For a list of all supported fetch policies, see [Supported fetch policies](https://www.apollographql.com/docs/react/data/queries/#supported-fetch-policies).

If you visit the profile page in your app, you'll notice that the query returns null. This is because we still need to implement login functionality. We'll tackle that in the next section!
